package com.ben.utils.ease

import android.view.animation.Interpolator

/**
 * Author: bin.yang
 * Maintainer: bin.yang
 * Date: 2022/4/24
 * Copyright: 2022 Inc. All rights reserved.
 * Desc: 贝塞尔曲线
 */
class EaseCubicInterpolator(x1: Float, y1: Float, x2: Float, y2: Float) : Interpolator {

    companion object {
        private const val ACCURACY = 4096
        private const val DEFAULT_LENGTH = 104

        private val arrayMap = mutableMapOf<String, List<Float>>()

        val EASE_OUT: EaseCubicInterpolator get() = EaseCubicInterpolator(0f, 0f, 0.58f, 1f)
        val EASE_IN: EaseCubicInterpolator get() = EaseCubicInterpolator(0.42f, 0f, 0f, 1f)
        val EASE_BOTH: EaseCubicInterpolator get() = EaseCubicInterpolator(0.42f, 0f, 0.58f, 1f)

        fun getKey(x1: Float, y1: Float, x2: Float, y2: Float): String {
            return "$x1-$y1-$x2-$y2"
        }
    }

    private val result by lazy {
        arrayListOf<Float>().apply {
            val key = getKey(x1, y1, x2, y2)
            if (arrayMap.containsKey(key)) {
                arrayMap[key]?.also { addAll(it) }
            } else {
                val dt = 1f / DEFAULT_LENGTH
                repeat(DEFAULT_LENGTH) {
                    add(getCubicCurvesNum(dt * it, x1, y1, x2, y2))
                }
                arrayMap[key] = this
            }
        }
    }

    private var mLastI = 0

    /**
     *  获取不同input贝塞尔曲线的值
     */
    private fun getCubicCurvesNum(input: Float, x1: Float, y1: Float, x2: Float, y2: Float): Float {
        var t = input
        for (i in mLastI until ACCURACY) {
            t = 1.0f * i / ACCURACY
            val x: Double = cubicCurves(t.toDouble(), 0.0, x1.toDouble(), x2.toDouble(), 1.0)
            if (x >= input) {
                mLastI = i
                break
            }
        }
        var value: Double = cubicCurves(t.toDouble(), 0.0, y1.toDouble(), y2.toDouble(), 1.0)
        if (value > 0.999) {
            value = 1.0
            mLastI = 0
        }
        return value.toFloat()
    }

    /**
     *  计算贝赛尔曲线
     */
    private fun cubicCurves(t: Double, value0: Double, value1: Double, value2: Double, value3: Double): Double {
        var value: Double
        val u = 1 - t
        val tt = t * t
        val uu = u * u
        val uuu = uu * u
        val ttt = tt * t
        value = uuu * value0
        value += 3 * uu * t * value1
        value += 3 * u * tt * value2
        value += ttt * value3
        return value
    }

    override fun getInterpolation(input: Float): Float {
        val index1 = (input / 0.01f).toInt()
        val index2 = index1 + 1
        return this.result[index1] + (this.result[index2] - this.result[index1]) * (input - index1.toFloat() * 0.01f) / 0.01f
    }
}
